package com.snail.controller;

import com.snail.constants.RedisConstants;
import com.snail.constants.SystemConstants;
import com.snail.exception.BizException;
import com.snail.model.dto.media.UploadFileResultDto;
import com.snail.model.vo.media.UploadFileVo;
import com.snail.model.vo.user.LoginUser;
import com.snail.result.Result;
import com.snail.result.ResultCode;
import com.snail.service.BizFilesService;
import com.snail.utils.RateLimiterUtil;
import com.snail.utils.SecurityUtils;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * @author Monica
 * @description 文件管理
 * @date 2025/8/12 17:43
 */
@Slf4j
@RestController
@RequestMapping("/file")

public class BizFilesController {
    @Autowired
    private BizFilesService bizFilesService;
    @Autowired
    private RateLimiterUtil rateLimiterUtil;


    private static final ExecutorService UPLOAD_EXECUTOR = new ThreadPoolExecutor(10, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), new ThreadFactory() {
        // 静态计数器，确保全局唯一计数
        private static final AtomicInteger COUNTER = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, "chunk-upload-" + COUNTER.getAndIncrement());
            thread.setDaemon(false); // 显式设置为非守护线程（默认也是非守护，可选）
            return thread;
        }
    }, new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * 上传图片
     *
     * @param file 上传的图片文件
     * @return 包含上传结果的响应
     */
    @PostMapping(path = "/uploadPic", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Result<UploadFileResultDto> uploadPic(@RequestParam("file") MultipartFile file) {


        LoginUser currentUser = SecurityUtils.getCurrentUser();
        Long userId = currentUser.getUserId();
        // 参数校验
        if (file == null || file.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_NULL);
        }
        // 校验限流
        boolean userMinuteAllowed = rateLimiterUtil.isAllowed("user:" + userId, 60, 5);
        boolean userHourAllowed = rateLimiterUtil.isAllowed("user:" + userId, 3600, 30);

        if (!userMinuteAllowed) {
            throw new BizException(ResultCode.USER_UPLOAD_FILE_ERROR, "1分钟内最多上传5张图片");
        }
        if (!userHourAllowed) {
            throw new BizException(ResultCode.USER_UPLOAD_FILE_ERROR, "1小时内最多上传30张图片");
        }


        if (file.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_NULL);
        }
        // 限制文件大小（如不超过10MB）
        long maxSize = 10 * 1024 * 1024;
        if (file.getSize() > maxSize) {
            throw new BizException(ResultCode.USER_UPLOAD_FILE_ERROR, "图片大小不能超过10MB");
        }

        // 准备上传参数
        UploadFileVo uploadFileParams = new UploadFileVo();
        uploadFileParams.setFileType(1);
        uploadFileParams.setFileName(file.getOriginalFilename());
        uploadFileParams.setFileSize(file.getSize());
        uploadFileParams.setUserId(currentUser.getUserId());
        // 创建临时文件并上传
        File tempFile = null;
        try {
            // 在指定目录下创建临时文件
            String tempFilePath = Paths.get(SystemConstants.TEMP_DIR).toAbsolutePath() + "/minio_" + UUID.randomUUID() + ".merge";
            tempFile = new File(tempFilePath);
            // 将上传文件内容复制到临时文件
            file.transferTo(tempFile);
            // 执行上传
            return bizFilesService.uploadFile(uploadFileParams, tempFile.getAbsolutePath());
        } catch (IOException e) {
            if (tempFile != null) {
                log.error("文件上传失败: {}", tempFile.getAbsolutePath(), e);
            }
            throw new BizException(ResultCode.USER_UPLOAD_FILE_ERROR);
        } finally {
            // 确保临时文件被删除
            if (tempFile != null && tempFile.exists()) {
                try {
                    Files.delete(tempFile.toPath());
                } catch (IOException e) {
                    log.error("删除临时文件失败: {}", tempFile.getAbsolutePath(), e);
                }
            }
        }
    }

    /**
     * 上传文档
     *
     * @param file 上传的文档文件
     * @return 包含上传结果的响应
     */
    @PostMapping(path = "/uploadDoc", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Result<UploadFileResultDto> uploadDoc(@RequestParam("file") MultipartFile file) {

        LoginUser currentUser = SecurityUtils.getCurrentUser();
        Long userId = currentUser.getUserId();
        // 参数校验
        if (file == null || file.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_NULL);
        }
        // 准备上传参数
        UploadFileVo uploadFileParams = new UploadFileVo();
        uploadFileParams.setFileType(2);
        uploadFileParams.setFileName(file.getOriginalFilename());
        uploadFileParams.setFileSize(file.getSize());
        uploadFileParams.setUserId(userId);
        // 创建临时文件并上传
        File tempFile = null;
        try {
            tempFile = new File(Paths.get(SystemConstants.TEMP_DIR).toAbsolutePath() + "/minio_" + UUID.randomUUID() + ".merge");
            // 将上传文件内容复制到临时文件
            file.transferTo(tempFile);
            // 执行上传
            return bizFilesService.uploadFile(uploadFileParams, tempFile.getAbsolutePath());
        } catch (IOException e) {
            if (tempFile != null) {
                log.error("文件上传失败: {}", tempFile.getAbsolutePath(), e);
            }
            throw new BizException(ResultCode.USER_UPLOAD_FILE_ERROR);
        } finally {
            // 确保临时文件被删除
            if (tempFile != null && tempFile.exists()) {
                try {
                    Files.delete(tempFile.toPath());
                } catch (IOException e) {
                    log.error("删除临时文件失败: {}", tempFile.getAbsolutePath(), e);
                }
            }
        }
    }

    /**
     * 删除文件
     *
     * @param fileMd5 文件md5
     * @return 删除结果
     */
    @PostMapping("/deleteFile")
    public Result<Boolean> deleteFile(String fileMd5) {
        return bizFilesService.deleteFile(fileMd5);
    }


    /**
     * 文件上传前检查文件
     *
     * @param fileMd5 文件md5
     * @return 文件是否存在
     */
    @PostMapping("/checkFile")
    public Result<Boolean> checkFile(@RequestParam("fileMd5") String fileMd5) {
        return bizFilesService.checkFile(fileMd5);
    }


    /**
     * 校验分块文件是否存在
     *
     * @param fileMd5     文件md5
     * @param chunk  分片数
     * @return 不存在的分块号
     */
    @PostMapping("/checkChunk")
    public Result<Boolean> checkChunk(@RequestParam("fileMd5") String fileMd5,
                                            @RequestParam("chunk") int chunk) {
        return bizFilesService.checkChunk(fileMd5, chunk);
    }


    @PostMapping(path = "/uploadChunk", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Result uploadChunk(
            @RequestParam("file") MultipartFile file,
            @RequestParam("fileMd5") String fileMd5,
            @RequestParam("chunk") int chunk) {


        // 参数校验
        if (file.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_NULL, "上传文件不能为空");
        }
        if (chunk < 0) {
            throw new BizException(ResultCode.PARAM_IS_NULL, "分块序号不能为负数");
        }

        File tempFile = null;
        try {
            // 创建临时文件（使用更安全的命名方式）
            tempFile = File.createTempFile("upload_" + fileMd5 + "_", "_chunk" + chunk);

            // 将上传文件内容复制到临时文件
            file.transferTo(tempFile);

            // 调用服务层处理分块上传
            return bizFilesService.uploadChunk(fileMd5, chunk, tempFile.getAbsolutePath());

        } catch (IOException e) {
            throw new BizException(ResultCode.PARAM_IS_NULL, "文件上传失败");
        } catch (Exception e) {
            throw new BizException(ResultCode.USER_UPLOAD_FILE_ERROR, "上传分块文件失败");
        } finally {
            // 确保临时文件被删除
            if (tempFile != null && tempFile.exists()) {
                try {
                    Files.delete(tempFile.toPath());
                } catch (IOException e) {
                    log.error("删除临时文件失败: {}", tempFile.getAbsolutePath(), e);
                }
            }
        }
    }


    @PostMapping("/mergeChunks")
    public Result mergeChunks(@RequestParam("fileMd5") String fileMd5,
                                    @RequestParam("fileName") String fileName,
                                    @RequestParam("chunkTotal") int chunkTotal) throws Exception {

        LoginUser currentUser = SecurityUtils.getCurrentUser();
        Long userId = currentUser.getUserId();
        UploadFileVo uploadFileParamsDto = new UploadFileVo();
        uploadFileParamsDto.setFileType(0);
        uploadFileParamsDto.setFileName(fileName);
        uploadFileParamsDto.setUserId(userId);

        return bizFilesService.mergeChunks(fileMd5, chunkTotal, uploadFileParamsDto);

    }
}
